package cn.zj.logistics.shiro;

import java.util.ArrayList;
import java.util.List;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import cn.zj.logistics.pojo.User;
import cn.zj.logistics.pojo.UserExample;
import cn.zj.logistics.pojo.UserExample.Criteria;
import cn.zj.logistics.service.PermissionService;
import cn.zj.logistics.service.UserService;

public class CustomRealm extends AuthorizingRealm {
	
	
	@Autowired
	private UserService userService;
	
	
	@Autowired
	private PermissionService permissionService;
	
	
	/*
	 * 授权方法，获取授权信息，开发者自己内部完成授权逻辑
	 * 
	 * 1，获取当前认证的身份：认证成功的user对象
	 * 
	 * 2, 根据user的角色信息，查询出对应的权限表达式
	 * 	一个用户一个角色，一个角色有多个权限（权限表达式）
	 * 3, 创建授权信息对象
	 * 		AuthorizationInfo，并且将第二步用户的权限表达式添加到授权信息对象中
	 * 4, 返回授权信息对象
	 * 
	 * 5, Shiro底层会将程序入口使用权限表达和授权方法当前身份添加到Shiro中的多个权限表达式匹配
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		
		User user = (User) principals.getPrimaryPrincipal();
		
		//获取用户的角色的权限的id，切割成数组
		String[] split = user.getPermissionIds().split(",");
		
		List<Long> permissionIds = new ArrayList<Long>();
		for (String permissionId : split) {
			permissionIds.add(Long.valueOf(permissionId));
		}
		//System.out.println("permissionIds :"+permissionIds);
		List<String> expressions = permissionService.selectExpressionByPermissionIds(permissionIds);
		
		//创建授权信息对象
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		//将权限表达式设置给授权信息对象
		authorizationInfo.addStringPermissions(expressions);
		
		//将当前user的角色设置给授权信息对象 /超级管理员，业务员，操作员....
		authorizationInfo.addRole(user.getRolename());
		
		return authorizationInfo;
	}

	/*
	 * 认证方法，获取认证信息，开发者自己内部完成认证逻辑
	 * 
	 * 认证逻辑分析
	 * 
	 * 1，注入UserService 层
	 * @Autowired
	 * private UserService userService;
	 * 
	 * 2,获取方法参数token的身份（账号）
	 * 3,调用service根据账号去数据库查询是否存在的方法
	 * User user = userService.selectUserByUsername(username);
	 * 	user==null: Shiro框架底层就会抛出：UnknownAccountException 账号不存在
	 *  user!=null;
	 * 4，如果user！=null 创建认证信息（AuthenticationInfo）对象，将数据库查询出的身份（user对象），凭证（密码）信息设置到身份信息对象中
	 * 
	 * 5,返回认证信息对象
	 * 
	 * 6，Shiro底层完成
	 * 		比对凭证（密码）
	 * 		Shiro底层回去拿数据库中查询的凭证（密码）和 token（外界传递进来）的凭证（密码）进行比对
	 * 	  比对成功：认证成功
	 *   比对失败，抛异常：IncorrectCredentialsException 密码错误异常	
	 *   
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		
		String username = (String) token.getPrincipal();
		
		UserExample example = new UserExample();
		Criteria criteria = example.createCriteria();
		criteria.andUsernameEqualTo(username);
		List<User> users = userService.selectByExample(example );
		//没有用户
		if(users.size() == 0) {
			return null;
		}
		//获取用户
		User user = users.get(0);
		//加密的密码
		Object hashedCredentials = user.getPassword();
		//密码对应加密的salt
		ByteSource credentialsSalt = ByteSource.Util.bytes(user.getSalt());
		
		
		return new SimpleAuthenticationInfo(user, hashedCredentials, credentialsSalt,this.getName());
	}

}
